package com.atguigu.gmall.cart.service.impl;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.entity.CartInfo;
import com.atguigu.gmall.cart.service.CartService;
import com.atguigu.gmall.common.config.utils.UserAuthUtil;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.feign.product.ProductSkuDetailFeignCilent;
import com.atguigu.gmall.product.entity.SkuInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

/**
 * @Description
 * @Return
 * @Author https://weiranyi.github.io
 * @Date Created in 2022-12-16 12:22
 **/
@Slf4j
@Service
public class CartServiceImpl implements CartService {
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    ProductSkuDetailFeignCilent skuFeignClient;
    @Autowired
    ThreadPoolExecutor executor;

    @Override
    public String determinCaryKey() {
        // [同一个线程调用]获取当前用户信息
        HttpServletRequest request = UserAuthUtil.request();
        String userId = request.getHeader(RedisConst.USER_ID_HEADER);
        if (!StringUtils.isEmpty(userId)) {
            //【购物车key】用用户id
            return RedisConst.CART_INFO_KEY + userId;
        }
        //【购物车key】用临时id
        String userTempId = request.getHeader(RedisConst.USER_TEMP_ID_HEADER);
        return RedisConst.CART_INFO_KEY + userTempId;
    }

    @Override
    public SkuInfo addToCart(Long skuId, Integer num, String cartKey) {
        SkuInfo skuInfo = null;
        // 1.【增&改】把商品添加到购物车，若之前没有则新增，若之前有则修改
        Boolean hasKey = redisTemplate.opsForHash().hasKey(cartKey, skuId.toString());
        if (!hasKey) {
            Long size = redisTemplate.opsForHash().size(cartKey);
            if (size > RedisConst.CART_ITEMS_LIMIT) {
                //购物车条目超过限制
                throw new GmallException(ResultCodeEnum.CART_ITEMS_OVERFLOW);
            }
            // 1.1 购物车没有该商品-则新增
            CartInfo cartInfo = new CartInfo();
            skuInfo = skuFeignClient.getSkuInfo(skuId).getData();
            cartInfo.setSkuId(skuId);
            cartInfo.setCartPrice(skuInfo.getPrice());
            cartInfo.setSkuPrice(skuInfo.getPrice());
            cartInfo.setSkuNum(num);
            cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
            cartInfo.setSkuName(skuInfo.getSkuName());
            cartInfo.setIsChecked(1);
            cartInfo.setCreateTime(new Date());
            cartInfo.setUpdateTime(new Date());
            saveCartItem(cartKey, cartInfo);

        } else {
            // 1.2 购物车有这个商品，只需要修改数量
            CartInfo cartInfo = getCartItem(skuId, cartKey);
            cartInfo.setSkuNum(cartInfo.getSkuNum() + num);
            // 1.2.3 存入redis
            saveCartItem(cartKey, cartInfo);
            // 前端只要商品名 图片 【数量已经在weball的request请求域中存储了】
            String skuName = cartInfo.getSkuName();
            String imgUrl = cartInfo.getImgUrl();
            skuInfo = new SkuInfo();
            skuInfo.setSkuName(skuName);
            skuInfo.setSkuDefaultImg(imgUrl);
        }
        // 为临时购物车设置过期时间
        if (StringUtils.isEmpty(UserAuthUtil.getUserIdHeader()) &&
                !StringUtils.isEmpty(UserAuthUtil.getUserTempIdHeader())
        ) {
            Long expire = redisTemplate.getExpire(cartKey);
            if (expire < 0) {
                // 没有设置过期时间就设置【只设置下面这句代码不加判断就会自动续期，按照最后一次进购物车开始算】
                redisTemplate.expire(cartKey, Duration.ofDays(365));
            }
        }
        return skuInfo;
    }


    @Override
    public List<CartInfo> getCartList(String cartKey) {
        // 1.【判断是否需要合并】如果是登录的情况-合并购物车
        String userId = UserAuthUtil.getUserIdHeader();
        try {
            if (!StringUtils.isEmpty(userId)) {
                String userTempId = UserAuthUtil.getUserTempIdHeader();
                List<Object> tempCartJson = redisTemplate.opsForHash().values(RedisConst.CART_INFO_KEY + userTempId);
                if (tempCartJson != null && tempCartJson.size() > 0) {
                    tempCartJson.stream().forEach((item -> {
                        CartInfo cartInfo = JSON.parseObject(item.toString(), CartInfo.class);
                        addToCart(cartInfo.getSkuId(), cartInfo.getSkuNum(), cartKey);
                    }));
                    redisTemplate.delete(RedisConst.CART_INFO_KEY + userTempId);
                }
            }
        } catch (Exception e) {
            // 如果出现临时购物车 + 用户购物车 > 200时，吞掉异常，保证业务正常运转
        }
        // 2.返回购物车商品
        List<Object> values = redisTemplate.opsForHash().values(cartKey);
        List<CartInfo> cartInfos = values.stream()
                .map((item) -> JSON.parseObject(item.toString(), CartInfo.class))
                .sorted((o1, o2) -> {
                    // 最后一个加入的排最上面
                    return o2.getCreateTime().compareTo(o1.getCreateTime());
                }) //判断
                .collect(Collectors.toList());
        CompletableFuture.runAsync(() -> {
            // 异步同步，最终让用户感知到即可
            this.batchUpdatePrice(cartKey, cartInfos);
        }, executor);
        return cartInfos;
    }

    /**
     * 批量修改商品价格
     *
     * @param cartKey
     * @param cartInfos
     */
    private void batchUpdatePrice(String cartKey, List<CartInfo> cartInfos) {
        cartInfos.stream().forEach((item) -> {
            BigDecimal price = skuFeignClient.getPrice(item.getSkuId()).getData();
            if (!(Math.abs(price.subtract(item.getCartPrice()).doubleValue()) < 0.001)) {
                log.info("{}购物车的{}商品价格和数据库不一致了，正在从数据库同步价格", cartKey, item.getSkuName());
                item.setSkuPrice(price);
                // 更新价格到redis
                saveCartItem(cartKey, item);
            }
        });
    }

    @Override
    public void updateCartItemNum(Long skuId, Integer num, String cartKey) {
        // 1.获取商品
        CartInfo cartItem = getCartItem(skuId, cartKey);
        // 2.修改
        cartItem.setSkuNum(cartItem.getSkuNum() + num);
        // 3.再次保存
        saveCartItem(cartKey, cartItem);
    }

    @Override
    public void toggleCartStat(Long skuId, Integer status, String cartKey) {
        CartInfo cartItem = getCartItem(skuId, cartKey);
        cartItem.setIsChecked(status);
        saveCartItem(cartKey, cartItem);
    }

    @Override
    public void deleteItem(Long skuId, String cartKey) {
        redisTemplate.opsForHash().delete(cartKey, skuId.toString());
    }

    @Override
    public void delChecked(String caryKey) {
        List<CartInfo> cartList = getCartList(caryKey);
        List<String> skuIds = cartList.stream()
                .filter(cartInfo -> cartInfo.getIsChecked() == 1)
                .map(item -> item.getSkuId().toString())
                .collect(Collectors.toList());
        if (skuIds != null && skuIds.size() > 0) {
            redisTemplate.opsForHash().delete(caryKey, skuIds.toArray());
        }
    }

    @Override
    public List<CartInfo> getChecked(String cartKey) {
        List<CartInfo> cartList = getCartList(cartKey)
                .stream().filter(cartItem -> cartItem.getIsChecked() == 1).collect(Collectors.toList());

        return cartList;
    }

    @Override
    public void deleteSkuIds(String cartKey, List<Long> skuIds) {
        String[] ids = skuIds.stream().map(item -> item.toString()).toArray(String[]::new);
        redisTemplate.opsForHash().delete(cartKey, ids);
    }

    /**
     * 获取购物车商品
     *
     * @param skuId
     * @param cartKey
     * @return
     */
    private CartInfo getCartItem(Long skuId, String cartKey) {
        // 1.2.1 得到redis中的数据
        String json = (String) redisTemplate.opsForHash().get(cartKey, skuId.toString());
        // 1.2.2 逆转json To 对象
        CartInfo cartInfo = JSON.parseObject(json, CartInfo.class);
        return cartInfo;
    }

    /**
     * 给购物车保存一个商品
     *
     * @param cartKey
     * @param cartInfo
     */
    private void saveCartItem(String cartKey, CartInfo cartInfo) {
        if (cartInfo.getSkuNum() > RedisConst.CART_ITEM_NUM_LIMIT) {
            // 商品数量超过200
            throw new GmallException(ResultCodeEnum.CART_ITEM_NUM_OVERFLOW);
        }
        redisTemplate.opsForHash().put(cartKey, cartInfo.getSkuId().toString(), JSON.toJSONString(cartInfo));
    }
}
